package com.example.controller;

import org.elasticsearch.action.search.MultiSearchResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import lombok.extern.slf4j.Slf4j;

import static org.elasticsearch.index.query.QueryBuilders.matchQuery;

/**
 * es查询练习
 *
 * @author 夏智峰
 * @create 2018-03-13 13:09
 */
@Slf4j
@RestController
@RequestMapping("/query")
public class ClientQueryController {
  @Autowired
  private TransportClient client;

  /**
   * @desc 简单的查询
   * @author 夏智峰
   * @create 2018/3/13 13:22
  **/
  @GetMapping("/search_all")
  public ResponseEntity searchAll() {
    SearchResponse response = client.prepareSearch().get();
    List<Map<String, Object>> mapList = new ArrayList<>();
    response.getHits().forEach(hit -> {
      log.info(hit.getSourceAsString());
      mapList.add(hit.getSourceAsMap());
    });
    return new ResponseEntity(mapList, HttpStatus.OK);
  }

  /**
   * @desc 按照作者名查询书籍信息
   * @author 夏智峰
   * @create 2018/3/13 13:41
  **/
  @GetMapping("/one/{index}/{type}")
  public ResponseEntity one(@PathVariable(value = "index") String index,
                            @PathVariable(value = "type") String type, String author) {
    SearchResponse response = client.prepareSearch(index)
            .setTypes(type)
            .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
            //使用match查询如果其他author上包含传入进来的某一个值就会被查询出来
            .setQuery(QueryBuilders.matchPhraseQuery("author", author))
            .setFrom(0).setSize(10).setExplain(true).get();

    List<Map<String, Object>> mapList = new ArrayList<>();
    response.getHits().forEach(hit -> {
      log.info(hit.getSourceAsString());
      mapList.add(hit.getSourceAsMap());
    });
    return new ResponseEntity(mapList, HttpStatus.OK);
  }

  /**
   * @desc 阅读滚动文档
   * 滚动文档：并不是为了实时的用户响应，而是为了处理大量的数据，
   * 例如，为了使用不同的配置来重新索引一个 index 到另一个 index 中去。
   * 后续的对文档的改动（索引、更新或者删除）都只会影响后面的搜索请求。
   * scroll-scan 的高效滚动
   * 使用 from and size 的深度分页，比如说 ?size=10&from=10000 是非常低
   * 效的，因为 100000 排序的结果必须从每个分片上取出并重新排序最后返回 10 条。这个过程需要对每个请求页重复。
   * scroll API 保持了哪些结果已经返回的记录，所以能更加高效地返回排序的结果。但是，按照默认设定排序结果仍然需要代价。
   * 一般来说，你仅仅想要找到结果，不关心顺序。你可以通过组合 scroll 和 scan 来关闭任何打分或者排序，以最高效的方式返
   * 回结果。你需要做的就是将 search_type=scan 加入到查询的字符串中：
   * @author 夏智峰
   * @create 2018/3/13 15:16
  **/
  @GetMapping("/scroll/{index}")
  public ResponseEntity scroll(@PathVariable(value = "index") String index, String auhtor) {

    QueryBuilder qb = matchQuery("author", auhtor);

    SearchResponse response = client.prepareSearch(index)
            .addSort(FieldSortBuilder.DOC_FIELD_NAME, SortOrder.ASC)
            .setScroll(new TimeValue(18000))
            .setQuery(qb)
            .setSize(2).get();
    List<Map<String, Object>> mapList = new ArrayList<>();

    do {
      response.getHits().forEach(hit -> {
        log.info(hit.getSourceAsString());
        mapList.add(hit.getSourceAsMap());
      });

      response = client.prepareSearchScroll(response.getScrollId())
              .setScroll(new TimeValue(18000))
              .execute()
              .actionGet();
    } while (response.getHits().getHits().length != 0);//零点击标记滚动结束和while循环。

    return new ResponseEntity(mapList, HttpStatus.OK);
  }

  /**
   * @desc 使用模板
   * @author 夏智峰
   * @create 2018/3/13 19:04
  **/
  @GetMapping("/multi/{index}")
  public ResponseEntity multi(@PathVariable(value = "index") String index) {
    SearchRequestBuilder srb1 = client.prepareSearch(index).setQuery(QueryBuilders
            .queryStringQuery("elasticsearch")).setSize(1);
    log.info(srb1.toString());

    SearchRequestBuilder srb2 = client.prepareSearch(index).setQuery(QueryBuilders
            .matchQuery("author", "不")).setSize(1);
    log.info(srb2.toString());

    MultiSearchResponse sr = client.prepareMultiSearch()
            .add(srb1)
            .add(srb2)
            .get();

    List<Map<String, Object>> mapList = new ArrayList<>();

    long nbHits = 0;
    for (MultiSearchResponse.Item item: sr.getResponses()) {
      SearchResponse response = item.getResponse();
      response.getHits().forEach(hit -> {
        log.info(hit.getSourceAsString());
        mapList.add(hit.getSourceAsMap());
      });
      nbHits += response.getHits().getTotalHits();
    }
    return new ResponseEntity(mapList, HttpStatus.OK);
  }

}
